Technical Note TN2104
Handling Audio Unit Events

目次

このテクニカルノートでは、AudioUnitEvent を使って、Audio Unit の状態の変更について、それが「ジェスチャ」によるものか、パラメータまたはプロパティの値の変更であるかにかかわらず、通知を送受信する方法について説明します。

AudioUnitEvent API を使うことで、Audio Unit、Audio Unit Host、およびそれらの UI は、イベントを統一的な方法で送受信できるようになりました。イベントの通知は、Carbon と Cocoa の両方の UI について別々に実装せずに送受信することができます。このため、Audio Unit、そのホスト、およびその UI 間のやり取りが大幅に簡素化されます。

[2004 年 2 月 16 日]

AudioUnitEvent カテゴリに分類されるイベントには、次のように 3 つのタイプがあります。

  • Audio Unit Property 値の変更。
  • Audio Unit Parameter 値の変更。
  • Audio Unit Parameter 変更の開始または終了ジェスチャの通知。

AudioUnitEvent API は、単にこのような変更の通知を送信するメカニズムです。この新しい実装は、Audio Unit プロパティとパラメータ検知に使われている既存の Core Audio API の上に構築されています。この新しい実装では、AudioUnitEvent を使って他のクライアントが何をしているかを認識することで 、クライアント間の連携を向上させることができます。Audio Unit ホストが Audio Unit のパラメータ(すなわち、プロパティ値)を変更すると、ホストの GUI にその変更を反映することができ、その逆もできるようになりました。

この新しい API では、パラメータ変更の開始ジェスチャや終了ジェスチャを伝える共通の通知手段も提供します。ジェスチャの例としては、UI のスライダをユーザがクリックしてドラッグする操作があります。この場合、開始ジェスチャはユーザによる 1 回目のクリックで、終了ジェスチャはユーザがマウスボタンを解放したときになります。これは Carbon のマウスクリックイベントによく似ていますが、ここではデベロッパがマウスクリック以外のイベント(キーボードのキーを押すことなど)にジェスチャの定義を拡張することができます。



AudioUnitEvent と AUEventListener

Audio Unit Event は AudioUnitEventType と引数で構成され、AudioUnitParameter または AudioUnitProperty のいずれかを参照できます。



リスト 1. AudioUnitUtilities.h で定義された AudioUnitEvent 構造体

typedef struct AudioUnitEvent {
   AudioUnitEventType        mEventType;
    union {
          AudioUnitParameter      mParameter;    
          AudioUnitProperty       mProperty;    
    }  mArgument;
} AudioUnitEvent;


AudioUnitEventType では、AudioUnitEvent について記述します。イベントの型は、開始および終了ジェスチャ、パラメータ変更、およびプロパティ変更の 4 種類に制限されています。AudioUnitEvent の使用目的は、AudioUnitParameter アクティビティに関する通知を送ることであり、使用できるイベント型は kAudioUnitEvent_ParameterValueChangekAudioUnitEvent_BeginParameterChangeGesture、または kAudioUnitEvent_EndParameterChangeGesture だけです。同様に、AudioUnitProperty はイベントの型として kAudioUnitEvent_PropertyChange のみを使用できます。



リスト 2. AudioUnitUtilities.h で定義された AudioUnitEventType

typedef UInt32 AudioUnitEventType;

enum {
    kAudioUnitEvent_ParameterValueChange           = 0,
    kAudioUnitEvent_BeginParameterChangeGesture    = 1,
    kAudioUnitEvent_EndParameterChangeGesture      = 2,
    kAudioUnitEvent_PropertyChange                 = 3
};



リスナーを作成して、Audio Unit のパラメータまたはプロパティの変更を監視することができます。AUEventListenerCreate メソッド(<AudioToolbox/AudioUnitUtilities.h> で定義されています)を使用して、Audio Unit の AUEventListener を作成します。AUEventListenerCreate メソッドでイベントリスナーを正しく作成するには、AudioUnitEvent 通知を受信する対象となる CFRunLoop と CFRunLoop モードを含める必要があります。これらの通知はリアルタイム優先順位のスレッドから発行されることがありますが、イベントリスナーの作成時に指定可能な、別の実行ループで受信することができます。AUEventListenerCreate を使うときには、イベントリスナープロシージャを呼び出す間隔と、イベントリスナープロシージャがパラメータ値変更をキューに入れる頻度を指定する必要があります。これにより、通知が発生する正しい頻度を定義でき、Audio Unit とホストはイベントに適切に反応するため、パフォーマンスが向上します。たとえば、連続する 2 つのイベントの間隔が、キューに入れられる頻度よりも小さい場合、リスナーは 2 番目のパラメータ変更についてのみ通知されます。作成する Audio Unit またはホストの型によっては、これが非常に役立つ場合があります。通知間隔と値変更の頻度に関する詳細とサンプルは、AudioUnitUtilities.h を参照してください。

イベントリスナーを作成した後、検知するイベントを AUEventListenerAddEventType を使って追加できます。AUEventListenerAddEventType メソッドには、AUEventListenerRefAudioUnitEvent が必要です。



リスト 3. AudioUnitUtilities.h に定義されている AUEventListenerProc コールバック

typedef void (*AUEventListenerProc) (void *        inCallbackRefCon,
                                     void *        inObject,
                                     const AudioUnitEvent *        inEvent,
                                     UInt64            inEventHostTime,
                                     Float32           inParameterValue);





リスト 4. AudioUnitUtilities.h に定義されている AUListenerCreate 宣言

extern OSStatus 
     AUListenerCreate(AUParameterListenerProc    inProc,
                         void *inRefCon,
                         CFRunLoopRef inRunLoop,
                         CFStringRef inRunLoopMode,
                         Float32 inNotificationInterval, 
                         AUParameterListenerRef *outListener);





リスト 5. Audio Unit イベントリスナーの作成

OSStatus createListener(){
    
    OSStatus result = noErr;
    AudioUnitProperty  myProperty;    
    AudioUnitEvent  myPropertyEvent;
    AUEventListenerRef MyListener;

    // 任意の実行ループを対象にできる
    CFRunLoopRef runLoop =
           (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop()); 
    CFStringRef loopMode = kCFRunLoopDefaultMode;
    
    myPropertyEvent.mEventType = kAudioUnitEvent_PropertyChange;
    myPropertyEvent.mArgument.mProperty = myProperty;
    
    AUEventListenerCreate(MyPropertyListener,
NULL, 
                                runLoop, 
                                loopMode, 
                                100,
                                100, 
                                &MyListener);
    AUEventListenerAddEventType(MyListener, NULL, &myPropertyEvent );
    
    return result;
}




先頭に戻る



パラメータ変更の処理

Audio Unit パラメータは、Audio Unit のレンダリングプロセスの動作を変更するのに使用します。パラメータを使って、ボリューム、ゲイン、ディレー、および Audio Unit に影響を与えるその他の動作を変更できます。これらの変更は多くの場合リアルタイムで適用され、変更の通知は AudioUnitEvent を通じて送受信できます。

パラメータ変更通知は、Audio Unit またはホストによって、AUEventListenerNotify を使って送信できます。AudioUnitEvent には、型として kAudioUnitEvent_ParameterValueChange、引数として AudioUnitParameter を指定する必要があります。イベントは、パラメータ変更を織り交ぜながら、イベントとパラメータ変更の時間的な順序を維持して、逐次リスナーに配送されます。これは AUParameterListener の動作によく似ていますが、より汎用的です。AUParameterListener は、AUParameterSetAUParameterListenerNotify を使ってこれらの通知を生成できます。

AUEventListener API は、パラメータ変更以外のイベントの意味を追加することで、上記の AUParameterListener API を拡張しています。



リスト 6. AudioUnitEvent(パラメータ変更)の作成と AUEventListener への通知の簡単なサンプル

    AudioUnitEvent myEvent;
    myEvent.mEventType = kAudioUnitEvent_ParameterValueChange;
    myEvent.mArgument.mParameter.mAudioUnit = myAudioUnit;
    myEvent.mArgument.mParameter.mParameterID = parameterID;
    myEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
    myEvent.mArgument.mParameter.mElement = 0;
    AUEventListenerNotify(NULL, NULL, &myEvent);



先頭に戻る



プロパティ変更の処理

プロパティは、Audio Unit との間で情報の受け渡しを行うための、一般的で拡張可能なメカニズムです。プロパティによって渡される情報の種類は、AudioUnitEvent を検知することで管理できます。Audio Unit の内部と外部の両方のアクションにより、プロパティの値を変更できます。

たとえば、「サウンド」環境設定パネルでユーザが選択するデバイスを追跡する出力オーディオユニットは Default Output Unit です。このデバイスは、ユーザとの対話を通していつでも変更できます。あるプログラムが DefaultOutputUnit のインスタンスを開いている場合、その変更をプログラムで検出するために、kAudioOutputUnitProperty_CurrentDevice プロパティのプロパティリスナーを設定することができます。あるいは、そのプログラムは、DefaultOutputUnit が接続されているデバイスには関心がなく、デバイスのフォーマット(たとえばサンプルレート)については関心があるかもしれません。変更されたプロパティを検出するために、リスナーは、プロパティ値が変更されたことを通知するようにあらかじめ設定することもできます。通知を受け取ったホストや Audio Unit は、必要とする適切なアクション取ることができます。

プロパティ変更通知は、AudioUnitEvent を使って実装できます。AudioUnitEventには、型として AudioUnitPropertykAudioUnitEvent_PropertyChange を指定する必要があります。AUEventListenerCreate を使ってリスナーを作成した後は、Audio Unit またはホストは、実際に変更が発生したスレッドではなく、最適なスレッドで安全にこれらの通知を受信できます。これは特に、オーディオレンダリングスレッド(通常はリアルタイムの優先順位で実行されます)で生成される通知の場合に役立ちます。



リスト 7. AudioUnitEvent(プロパティ変更)の作成と AUEventListener への通知に関する簡単なサンプル

    AudioUnitEvent myEvent;
    myEvent.mEventType = kAudioUnitEvent_PropertyChange;
    myEvent.mArgument.mProperty.mAudioUnit = myAudioUnit;
    myEvent.mArgument.mProperty.mPropertyID = PropertyID;
    myEvent.mArgument.mProperty.mScope = kAudioUnitScope_Global;
    myEvent.mArgument.mProperty.mElement = 0;
    AUEventListenerNotify(NULL, NULL, &myEvent);



先頭に戻る



開始/終了ジェスチャの通知

Audio Unit には、開始および終了ジェスチャの通知を送信する汎用的な方法があります。開始および終了ジェスチャ通知は、パラメータが変更されようとしているか、あるいは変更を終えたことを伝える通知を送信します。これにより、Carbon UI と Cocoa UI は、ユーザとの対話からこの型のイベント通知を送受信したり、その情報で実行することを決めることができます。この新しいメカニズムはジェスチャの定義を一般化しているため、GUI とのすべての対話をジェスチャとして解釈でき、その結果、解釈できる唯一の有効なジェスチャとしてのマウスクリック(マウスアップ、マウスダウン)の制限は取り除かれています。このことにより、Carbon ビューイベントの kAudioUnitCarbonViewEvent_MouseDownInControlkAudioUnitCarbonViewEvent_MouseUpInControl は最終的には廃止される予定です。

たとえば、ユーザは Audio Unit の GUI 上でパラメータボタンをクリックしてドラッグすることにより、その値を変更したいことが考えられます。パラメータ値が変更されようとしていることをユーザにフィードバックするボタンカラーを変更することによって、この UI を、ユーザの選択を反映するように作成することができます。パラメータボタンが解放された後、パラメータ変更の終了ジェスチャ通知を送信して、ボタンカラーを元に戻すことができます。

開始および終了ジェスチャの通知には、データは関連付けられません。AUEventListenerNotify とイベント型の kAudioUnitEvent_BeginParameterChangeGesture または kAudioUnitEvent_EndParameterChangeGesture を使うと、パラメータの引数値があっても無視されます。型が kAudioUnitEvent_ParameterValueChange のイベントの場合、AudioUnitParameter データは AudioUnitEvent を通じてのみ受け渡されます。



リスト 8. 開始および終了ジェスチャを使ったパラメータ値変更を管理する簡単なサンプル

void CreateAudioUnitEventForParameterID(AudioUnitEvent *myEvent,
                                        AudioUnitParameterID parameterID){

    myEvent->mArgument.mParameter.mAudioUnit = myAudioUnit;
    myEvent->mArgument.mParameter.mParameterID = parameterID;
    myEvent->mArgument.mParameter.mScope = kAudioUnitScope_Global;
    myEvent->mArgument.mParameter.mElement = 0;

}

void HandleMouseDown(AudioUnitEvent *myEvent){
    myEvent->mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
    AUEventListenerNotify(NULL, NULL, myEvent);
}

void HandleParameterChange(AudioUnitEvent *myEvent){
    myEvent->mEventType =kAudioUnitEvent_ParameterValueChange;
    AUEventListenerNotify(NULL, NULL, myEvent);
}

void HandleMouseUp(AudioUnitEvent *myEvent){
    myEvent->mEventType = kAudioUnitEvent_EndParameterChangeGesture;
    AUEventListenerNotify(NULL, NULL, myEvent);
}




先頭に戻る



要約

AudioUnitEvent API により、Audio Unit、Audio Unit ホスト、およびそれらの GUI はより効果的に通信できます。結果として、これらのオブジェクト間の対話は大幅に簡略化されます。

先頭に戻る



参考資料

Audio and MIDI on Mac OS X (May 2001)